/* Examples/cauchy_02.cpp
 * James S. Plank

Jerasure - A C/C++ Library for a Variety of Reed-Solomon and RAID-6 Erasure Coding Techniques
Copright (C) 2007 James S. Plank

James S. Plank
Department of Electrical Engineering and Computer Science
University of Tennessee 
Knoxville, TN 37996
plank@cs.utk.edu
*/

/*
 * $Revision: 1.2 $
 * $Date: 2008/08/19 17:41:40 $ 
 */


/*
	revised by S. Simmerman
	2/25/08  

  Jerasure 2.0
  6/1/11

*/
#include <iostream>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jerasure-2.h"
#include "cauchy-2.h"

#define talloc(type, num) (type *) malloc(sizeof(type)*(num))

typedef vector <unsigned int> IVec;

void usage(char *s)
{
  fprintf(stderr, "usage: cauchy_02 k m w - Scheduled CRS coding example with the original matrix in  GF(2^w).\n");
  fprintf(stderr, "       \n");
  fprintf(stderr, "       k+m must be <= 2^w.  It sets up a Cauchy distribution matrix using the original\n");
  fprintf(stderr, "       Cauchy Distribution matrix construction algorithm, then it encodes\n");
  fprintf(stderr, "       k devices of w*%ld bytes using smart bit-matrix scheduling.\n", sizeof(uint64_t));
  fprintf(stderr, "       It decodes using bit-matrix scheduling as well.\n");
  fprintf(stderr, "       \n");
  fprintf(stderr, "This demonstrates: cauchy_original_coding_matrix()\n");
  fprintf(stderr, "                   cauchy_n_ones()\n");
  fprintf(stderr, "                   cauchy_xy_coding_matrix()\n");
  fprintf(stderr, "                   JER_BM_To_Schedule_CSHR()\n");
  fprintf(stderr, "                   JER_Schedule_Encode()\n");
  fprintf(stderr, "                   JER_Schedule_Decode_Lazy()\n");
  fprintf(stderr, "                   JER_Get_Stats()\n");
  if (s != NULL) fprintf(stderr, "%s\n", s);
  exit(1);
}

static void print_data_and_coding(JER_Slices * slices)
{
  int i, j, x, n, sp;
  uint64_t l;
  int k,m,w,psize;

  k = slices->K;
  m = slices->M;
  w = slices->W;
  psize = slices->PS;

  if(k > m) n = k;
  else n = m;
  //the coding column is placed sp spaces from the left
  //13 spaces are just for the labels and separators
  //2 spaces per byte so psize*2
  sp = psize*2 + 13;

  printf("%-*sCoding\n", sp, "Data");
  for(i = 0; i < n; i++) {
    for (j = 0; j < w; j++) {
      if(i < k) {
        if(j==0) printf("D%-2d p%-2d:", i,j);
        else printf("    p%-2d:", j);
        for(x = 0; x < psize; x +=sizeof(uint64_t)) {
          memcpy(&l, slices->ptrs[i]+j*psize+x, sizeof(uint64_t));
          printf(" %016lx", l);
        }
        printf("    ");
      }
      else printf("%*s", sp, "");
      if(i < m) {
        if(j==0) printf("C%-2d p%-2d:", i,j);
        else printf("    p%-2d:", j);
        for(x = 0; x < psize; x +=sizeof(uint64_t)) {
          memcpy(&l, slices->ptrs[i+k]+j*psize+x, sizeof(uint64_t));
          printf(" %016lx", l);
        }

      }
      printf("\n");
    }
  }

  printf("\n");
}

int main(int argc, char **argv)
{
	uint64_t l;
  int bll_equal;
	int k, w, i, j, m,sz;
	int no;
  JER_Matrix * matrix, *m2;
	JER_Bitmatrix *bitmatrix;
	IVec X, Y;
	vector <int> erasures, erased;
	JER_Slices *slices;
	vector <double> stats;
	JER_Schedule *smart;
  
  if (argc != 4) usage(NULL);
  if (sscanf(argv[1], "%d", &k) == 0 || k <= 0) usage((char *)"Bad k");
  if (sscanf(argv[2], "%d", &m) == 0 || m <= 0) usage((char *)"Bad m");
  if (sscanf(argv[3], "%d", &w) == 0 || w <= 0 || w > 32) usage((char *)"Bad w");
  if (w < 30 && (k+m) > (1 << w)) usage((char *)"k + m is too big");

  matrix = cauchy_original_coding_matrix(k, m, w);
  if (matrix == NULL) {
    usage((char *)"Couldn't make coding matrix");
  }

  no = 0;
  for (i = 0; i < m; i++) {
		for (j = 0; j < k; j++) {
			no += cauchy_n_ones(matrix->Elts[i][j], w);
		}
  }
  printf("Matrix has %d ones\n\n", no);
	matrix->Print();
  printf("\n");

	bitmatrix = new (nothrow) JER_Bitmatrix(*matrix);
	if(bitmatrix == NULL) {
		cerr << "Bad Allocation of JER_Bitmatrix\n";
		exit(1);
	}

  smart = JER_BM_To_Schedule_CSHR(bitmatrix);

  srand48(0);
  slices = new (nothrow) JER_Slices();
	slices->K = k;
	slices->M = m;
	slices->W = w;
	slices->PS = sizeof(uint64_t);
	slices->size = w*slices->PS;
	slices->ptrs.resize(k + m);

  //put original data in slices
  for (i = 0; i < k; i++) {
    slices->ptrs[i] = talloc(unsigned char, sizeof(uint64_t)*w);
    for (j = 0; j < w; j++) {
      l = (uint64_t)lrand48();
      l <<= 8*4;
      l += (uint64_t)lrand48();
      memcpy(slices->ptrs[i]+j*sizeof(uint64_t), &l, sizeof(uint64_t));
    }
  }

  //allocate space for the slices of coding data
  for (i = k; i < k+m; i++) {
    slices->ptrs[i] = talloc(unsigned char, sizeof(uint64_t)*w);
    //initialize them to 0
    bzero(slices->ptrs[i],sizeof(uint64_t)*w);
  }

  JER_Schedule_Encode(slices,smart);
  stats.resize(3);
  JER_Get_Stats(stats);
  printf("Smart Encoding Complete: - %.0lf XOR'd bytes\n\n", stats[0]);
	print_data_and_coding(slices);

  //erase m random disks
  erasures.resize(m);
  erased.resize(k+m,0);
  for (i = 0; i < m; ) {
    erasures[i] = lrand48()%(k+m);
    if (erased[erasures[i]] == 0) {
      erased[erasures[i]] = 1;
      bzero(slices->ptrs[erasures[i]],sizeof(uint64_t)*w);
      i++;
    }
  }
  printf("Erased %d random devices:\n\n", m);
  print_data_and_coding(slices);

  JER_Schedule_Decode_Lazy(slices,bitmatrix, erasures, 1);
  JER_Get_Stats(stats); 

  printf("State of the system after decoding: %.0lf XOR'd bytes\n\n", stats[0]);
  print_data_and_coding(slices);

  X.resize(m);
  Y.resize(k);
  if (X.size() != m || Y.size() != k) { perror("resize"); exit(1); }
  for (i = 0; i < m; i++) X[i] = i;
  for (i = 0; i < k; i++) Y[i] = m+i;
  m2 = cauchy_xy_coding_matrix(k, m, w, X, Y);
  
  //check that matrix and m2 are identical
  bll_equal = 1;
  //only check if the sizes are the same
  if(matrix->Elts.size() == m2->Elts.size()){
    //check all of the vectors of ints
    for(i=0;i<matrix->Elts.size();i++){
      //check all of the numbers in this vector
      sz = matrix->Elts[i].size();
      if(sz != m2->Elts[i].size()){
        bll_equal = 0;
        break;
      }
      for(j=0;j<sz;j++){
        if(matrix->Elts[i][j] != m2->Elts[i][j]){
          bll_equal = 0;
          break;
        }
      }
      if(!bll_equal){
        break;
      }
    }
  }else{
    bll_equal = 0;
  }

  if(!bll_equal){
    printf("Error -- the matrices made by original and xy don't match\n");
    exit(1);
  }else{
    printf("Generated the identical matrix using cauchy_xy_coding_matrix()\n");
  }

  return 0;
}
